#pragma once

#include "AttendanceListener.h"
#include "GameConfig.h"
#include "GameEngine.h"
#include "GameStateEvaluator.h"
#include "Minimax.h"

struct GrudgeDataRow
{
	GamePlayer*	recordHolder;
	int			score;
}; // end struct

class GrudgeEvaluator :
	public GameStateEvaluator,
	public AttendanceListener
{
public:
					GrudgeEvaluator(string& accountName) :
					  recordCount(NO_RECORDS)
					{
						setAccountName(accountName);
					} // end constructor

	/*
		Evaluation of a game-state considers both Grudge Values and Game Scores.
		The value that is being returned is a reflection of how happy we are with the outcome.
		If a player in poor standing is losing points, we're happy.
		If a player in good standing is gaining points, we're happy.
		If a player in poor standing is gaining points, we're unhappy.
		Finally, if a player in good standing is losing points, we're unhappy.
		Each players' evaluation contributes, through simple addition, to the total evaluation.
		@param state	a proposed game-board layout
		@return			the score that the state merits due to its effect on the players
	*/
	virtual
	int				evaluate(GameState* state)
					{
						int total = 0;
						for (int seat = recordCount; seat--;)
						{
							int grudgeValue = records[seat].score;

							int individualGain = (grudgeValue < 0
								? evaluationBasis[seat] - state->score(seat)
								: state->score(seat) - evaluationBasis[seat]
							);

							total += individualGain * grudgeValue * grudgeValue;
						} // end loop: per-player contribution

						return total;
					} // end GameStateEvaluator method

	/*
		When the GameEngine reports a change in GameState,
		our position is that the transition was a result of a purposeful decision,
		and there will be a consequence to that decision!
		Task 1: Adjust the Grudge Value of the player who was responsible for the state change.
		Task 2: Record the current scores so that they are available to comparison in evaluate().
		@param preceding	"from" state, which is being replaced by impending
		@param impending	"to" state, which is the replacement for preceding
	*/
	virtual
	void			observeStateTransition(
						SeatNumber	seatResponsible,
						GameState*	preceding,
						GameState*	impending
					) {
						int seat = recordCount;
						for (int* playerScore = evaluationBasis + recordCount;
							--playerScore, seat--;
						)
							*playerScore = impending->score(seat);
					} // end GamePlayer method

	virtual
	void			participantCountSet(SeatNumber count)
					{
						if (NULL != records)
							flushRecords();

						records = new GrudgeDataRow[count];
						evaluationBasis = new int[count];
					} // end AttendanceListener method

	virtual
	void			participantIntroduced(
						SeatNumber	seatNum,
						GamePlayer*	who
					) {
						if (NULL != records[seatNum].recordHolder)
							saveRecord(seatNum);

						loadRecord(seatNum, who);
					} // end AttendanceListener method

	virtual
	void			participantLeft(
						SeatNumber	seatNum,
						GamePlayer*	who
					) {
						saveRecord(seatNum);
					} // end AttendanceListener method

	virtual
	void			startTurn(
						Authority*	key
					) {
						auto&	engine = GameEngine::instance();
						Minimax brain(this);

						engine.enact(
							brain.getBestAction(engine.getCurrentState()),
							key
						);
					} // end GamePlayer method

protected:	
	void			flushRecords()
					{
						if (NO_RECORDS == recordCount)
							return;

						for (int seat = recordCount; seat-- != 0;)
							saveRecord(seat);

						delete [] records;
						delete [] evaluationBasis;
					} // end method

	void			loadRecord(
						SeatNumber	seatNumber,
						GamePlayer*	who
					) {
						loadOrSave(true, seatNumber, who);
						records[seatNumber].recordHolder = who;
					} // end method

	void			saveRecord(SeatNumber seatNumber)
					{
						loadOrSave(false, seatNumber, records[seatNumber].recordHolder);
					} // end method

private:
	//friend class boost::serialization::access;
	friend class GrudgeConfig;

	static const
	int				NO_RECORDS = -1;

	SeatNumber		recordCount;
	GrudgeDataRow*	records;

	// Game scores (not Grudge Values) from the current round.
	int*			evaluationBasis;

	void			loadOrSave(
						bool		doTheFirstThing,
						SeatNumber	seatNumber,
						GamePlayer*	who
					) {
						if (NULL == who) return;

						std::swap(seatNumber, recordCount);

						GrudgeConfig* cfg = GrudgeConfig::of(this);	
						try {
							doTheFirstThing
								? cfg->forRecord(who)->load()
								: cfg->forRecord(who)->save();
						}
						catch(...)
						{
						}
						delete cfg;

						recordCount = seatNumber;
					} // end helper method
}; // end class
